{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "db68f9ed-b3d6-4722-92da-d23681ceaab2",
   "metadata": {},
   "source": [
    "# Exercise 1\n",
    "\n",
    "\n",
    "## Supervised Learning- Classification Models\n",
    "\n",
    "---\n",
    "\n",
    "## Overview\n",
    "\n",
    "Welcome to this excercise. We are now going to use our new skills to build our supervised learning models that use classification approach.\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2c8ab178-0eef-4f65-95f3-bbb26c72cda4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "\n",
    "# Manually set the path relative to the py file's location that you want to import\n",
    "func_lib_path = os.path.abspath(os.path.join(os.getcwd(), '../../'))# Add the path to sys.path\n",
    "sys.path.append(func_lib_path)\n",
    "\n",
    "# Now you can import func_lib\n",
    "import func_lib\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "import statsmodels.api as sm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f652fad8-99c3-47d3-8637-b21d9e852d03",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[*********************100%%**********************]  501 of 501 completed\n",
      "\n",
      "1 Failed download:\n",
      "['SW']: YFChartError(\"%ticker%: Data doesn't exist for startDate = 946702800, endDate = 1714536000\")\n"
     ]
    }
   ],
   "source": [
    "# Retrieve historical price data.\n",
    "# - Use the 'createHistPrices()' function from 'func_lib' to get historical price data.\n",
    "# - Assign the result to the variable 'historical_prices'.\n",
    "\n",
    "historical_prices = func_lib.create_hist_prices()\n",
    "\n",
    "# Define the list of momentum periods.\n",
    "# - Create a list containing different time periods for calculating momentum indicators.\n",
    "# - Assign this list to the variable 'list_of_momentums'.\n",
    "\n",
    "list_of_momentums = [1, 5, 15, 20]\n",
    "\n",
    "# Compute returns based on historical prices and momentum periods.\n",
    "# - Use the 'computingReturns()' function from 'func_lib' with 'historical_prices' and 'list_of_momentums' as inputs.\n",
    "# - Assign the result to the variable 'total_returns'.\n",
    "\n",
    "total_returns = func_lib.compute_returns(historical_prices, list_of_momentums)\n",
    "\n",
    "# Remove any rows with missing values from the returns data.\n",
    "# - Use the 'dropna()' method to remove rows with NaN values from 'total_returns'.\n",
    "# - Apply the change in place to modify the DataFrame directly.\n",
    "\n",
    "total_returns.dropna(inplace=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c0a91e8f-367e-42c0-9a48-e232a859d882",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>F_1_d_returns</th>\n",
       "      <th>1_d_returns</th>\n",
       "      <th>5_d_returns</th>\n",
       "      <th>15_d_returns</th>\n",
       "      <th>20_d_returns</th>\n",
       "      <th>F_1_d_returns_Ind</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Ticker</th>\n",
       "      <th>Date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th rowspan=\"5\" valign=\"top\">A</th>\n",
       "      <th>2000-02-01</th>\n",
       "      <td>0.077465</td>\n",
       "      <td>0.072710</td>\n",
       "      <td>0.048938</td>\n",
       "      <td>0.029919</td>\n",
       "      <td>-0.013889</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2000-02-02</th>\n",
       "      <td>0.016340</td>\n",
       "      <td>0.077465</td>\n",
       "      <td>0.119854</td>\n",
       "      <td>0.125000</td>\n",
       "      <td>0.150376</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2000-02-03</th>\n",
       "      <td>-0.019293</td>\n",
       "      <td>0.016340</td>\n",
       "      <td>0.136073</td>\n",
       "      <td>0.166979</td>\n",
       "      <td>0.246493</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2000-02-04</th>\n",
       "      <td>0.042623</td>\n",
       "      <td>-0.019293</td>\n",
       "      <td>0.120294</td>\n",
       "      <td>0.127542</td>\n",
       "      <td>0.270834</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2000-02-07</th>\n",
       "      <td>-0.006290</td>\n",
       "      <td>0.042623</td>\n",
       "      <td>0.201133</td>\n",
       "      <td>0.162706</td>\n",
       "      <td>0.223077</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                   F_1_d_returns  1_d_returns  5_d_returns  15_d_returns  \\\n",
       "Ticker Date                                                                \n",
       "A      2000-02-01       0.077465     0.072710     0.048938      0.029919   \n",
       "       2000-02-02       0.016340     0.077465     0.119854      0.125000   \n",
       "       2000-02-03      -0.019293     0.016340     0.136073      0.166979   \n",
       "       2000-02-04       0.042623    -0.019293     0.120294      0.127542   \n",
       "       2000-02-07      -0.006290     0.042623     0.201133      0.162706   \n",
       "\n",
       "                   20_d_returns  F_1_d_returns_Ind  \n",
       "Ticker Date                                         \n",
       "A      2000-02-01     -0.013889                  1  \n",
       "       2000-02-02      0.150376                  1  \n",
       "       2000-02-03      0.246493                  0  \n",
       "       2000-02-04      0.270834                  1  \n",
       "       2000-02-07      0.223077                  0  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Convert 'F_1_d_returns' to binary values based on whether they are positive or not.\n",
    "# - Use the 'apply()' function with a lambda expression to create a new column 'F_1_d_returns_Ind'.\n",
    "# - The lambda expression assigns 1 if the value is greater than 0, otherwise assigns 0.\n",
    "\n",
    "total_returns['F_1_d_returns_Ind'] = total_returns['F_1_d_returns'].apply(lambda x: 1 if x > 0 else 0)\n",
    "# Display the updated DataFrame.\n",
    "total_returns.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8f5424f8-bea9-46d6-b2b1-5ae6934180d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2017-01-20 00:00:00')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Determine the split index for 70% of the dates.\n",
    "# - Extract the unique dates from the index of 'total_returns'.\n",
    "# - Calculate the split date by indexing into the unique dates to get the date corresponding to 70% of the total length of unique dates.\n",
    "# - Assign this date to the variable 'split_date' and display it.\n",
    "\n",
    "unique_dates = total_returns.index.get_level_values('Date').unique()\n",
    "split_date = unique_dates[int(0.7 * len(unique_dates))]\n",
    "print(split_date)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7f011cc5-a9b6-4e57-9bc0-8c4478e6201c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the training set: all data before the split date.\n",
    "# - Use the 'loc[]' indexer to select rows from 'total_returns' where the date in the index is earlier than 'split_date'.\n",
    "# - Assign this subset of data to the variable 'train_data'.\n",
    "\n",
    "\n",
    "# Create the testing set: all data from the split date onwards.\n",
    "# - Use the 'loc[]' indexer to select rows from 'total_returns' where the date in the index is equal to or later than 'split_date'.\n",
    "# - Assign this subset of data to the variable 'test_data'.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "25b5cac1-451f-4ddf-91c8-9d2827cd39bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Extract the target variable for testing.\n",
    "# - Select the 'F_1_d_returns' column from the 'test_data' DataFrame.\n",
    "# - Assign this column to the variable 'total_returns'.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "9a100112-7de7-41a9-aeb8-f9f812c68603",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the features and target variables.\n",
    "# - Specify the columns to be used as features ('1_d_returns', '5_d_returns', '15_d_returns', '20_d_returns').\n",
    "# - Specify the column to be used as the target variable ('F_1_d_returns_Ind').\n",
    "\n",
    "\n",
    "\n",
    "# Split the data into training and testing sets.\n",
    "# - Extract the feature columns from the training data and testing data.\n",
    "# - Extract the target column from the training data and testing data.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3b6913ea-571f-4e7a-9cdd-b7e2c88a2149",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Standardize the features.\n",
    "# - Create an instance of 'StandardScaler' for scaling the features.\n",
    "# - Fit the scaler to the training data and transform both the training and testing feature sets.\n",
    "# - Convert the scaled arrays back to DataFrames with the same index and column names as the original feature sets.\n",
    "\n",
    "\n",
    "# Scale the training features\n",
    "\n",
    "# Scale the testing features\n",
    "\n",
    "\n",
    "# Convert the scaled arrays back to DataFrames\n",
    "X_train_scaled = pd.DataFrame(X_train_scaled, index=X_train.index, columns=X_train.columns)\n",
    "X_test_scaled  = pd.DataFrame(X_test_scaled, index=X_test.index, columns=X_test.columns)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "ddf84714-4fe4-4880-88ab-9f0dfa74491d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully.\n",
      "         Current function value: 0.692570\n",
      "         Iterations 4\n",
      "                           Logit Regression Results                           \n",
      "==============================================================================\n",
      "Dep. Variable:      F_1_d_returns_Ind   No. Observations:              1795319\n",
      "Model:                          Logit   Df Residuals:                  1795314\n",
      "Method:                           MLE   Df Model:                            4\n",
      "Date:                Mon, 02 Sep 2024   Pseudo R-squ.:               0.0006055\n",
      "Time:                        00:15:57   Log-Likelihood:            -1.2434e+06\n",
      "converged:                       True   LL-Null:                   -1.2441e+06\n",
      "Covariance Type:            nonrobust   LLR p-value:                     0.000\n",
      "================================================================================\n",
      "                   coef    std err          z      P>|z|      [0.025      0.975]\n",
      "--------------------------------------------------------------------------------\n",
      "const            0.0355      0.001     23.782      0.000       0.033       0.038\n",
      "1_d_returns     -0.0288      0.002    -17.338      0.000      -0.032      -0.026\n",
      "5_d_returns     -0.0270      0.002    -13.960      0.000      -0.031      -0.023\n",
      "15_d_returns    -0.0285      0.003     -9.749      0.000      -0.034      -0.023\n",
      "20_d_returns     0.0122      0.003      4.419      0.000       0.007       0.018\n",
      "================================================================================\n"
     ]
    }
   ],
   "source": [
    "# Add a constant to the model (intercept).\n",
    "# - Use the 'add_constant()' function from the statsmodels library to add an intercept term to the feature sets.\n",
    "# - Apply this to both the training and testing feature sets.\n",
    "\n",
    "X_train_scaled_const_add = sm.add_constant(X_train_scaled)\n",
    "X_test_scaled_const_add  = sm.add_constant(X_test_scaled)\n",
    "\n",
    "# Create and fit a logistic regression model.\n",
    "# - Initialize a logistic regression model using 'sm.Logit()' with the target and feature sets including the intercept.\n",
    "# - Fit the model to the training data.\n",
    "\n",
    "model = sm.Logit(y_train, X_train_scaled_const_add).fit()\n",
    "\n",
    "# Print the model summary.\n",
    "# - Use 'summary()' to display the detailed results and statistics of the fitted model.\n",
    "\n",
    "print(model.summary())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4828df57-416c-43f0-b99c-b24bc83a74f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set the model name.\n",
    "# - Assign the name 'Log' to the variable 'model_name' to label the regression model.\n",
    "\n",
    "\n",
    "# Make predictions using the fitted model.\n",
    "# - Use 'model.predict()' to generate predictions based on the scaled testing features with the added constant.\n",
    "\n",
    "\n",
    "# Create DataFrames for the actual and predicted values.\n",
    "# - Convert the true target values 'y_test' and predicted values 'y_pred' to DataFrames.\n",
    "# - Rename the column in 'y_pred_df' to match the model name.\n",
    "\n",
    "y_test_df = pd.DataFrame(y_test)\n",
    "y_pred_df = pd.DataFrame(y_pred)\n",
    "y_pred_df.rename(columns={0: model_name}, inplace=True)\n",
    "\n",
    "# Combine the actual and predicted values into one DataFrame.\n",
    "# - Use 'pd.merge()' to merge 'y_pred_df' with 'total_returns' on their index.\n",
    "# - This combines the predicted values with the actual returns data.\n",
    "\n",
    "y_test_and_pred = pd.merge(y_pred_df, total_returns, left_index=True, right_index=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "5ea1e907-5b59-4d2e-83ba-dfe4b8262e7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define a trading strategy based on logistic regression predictions.\n",
    "# - Create a function 'trading_strategy' that takes the predicted value 'y_pred' as input.\n",
    "# - If the predicted value is greater than 0.5, return 1 (indicating a \"Go long\" signal).\n",
    "# - Otherwise, return 0 (indicating no action or \"Do nothing\").\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "f76b8327-20eb-49b8-84c1-2a0016340cee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 50736 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The CAGR is: 16.05%\n",
      "Sharpe Ratio of Strategy: 0.85\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 15516 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Compute performance metrics based on the predictions and actual returns.\n",
    "# - Use the 'func_lib.compute_BM_Perf()' function to calculate cumulative and calendar returns.\n",
    "# - Pass the DataFrame 'y_test_and_pred' containing the actual and predicted values as an argument.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "83b115af-3b27-40b2-93f0-2b9cc2fd0db2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 62512 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The CAGR is: 14.88%\n",
      "Sharpe Ratio of Strategy: 0.9\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 17496 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Compute strategy performance metrics.\n",
    "# - Use the 'func_lib.compute_strat_perf()' function to calculate the performance of the trading strategy.\n",
    "# - Pass the following arguments:\n",
    "#   - 'y_test_and_pred': The DataFrame containing actual and predicted values.\n",
    "#   - 'cum_returns': Cumulative returns calculated previously.\n",
    "#   - 'calendar_returns': Calendar returns calculated previously.\n",
    "#   - 'trading_strategy': The function defining the trading strategy based on predictions.\n",
    "#   - 'model_name': The name of the model to include in the performance evaluation.\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
